/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_MATRIX4_H
#define M_MATRIX4_H

#include <stdexcept>
#include "vector3.h"
#include <cmath>

namespace Engine
{
	/**
	*   Class encapsulating a standard 4x4 homogeneous matrix.
	*/
	template<class Real>
    class  Matrix4
    {
    public:
		/**********************************************************************************************************/
        /**
		*   Default constructor.
		*/
		inline Matrix4()
        {
			m[0][0] = 0;
            m[0][1] = 0;
            m[0][2] = 0;
            m[0][3] = 0;
            m[1][0] = 0;
            m[1][1] = 0;
            m[1][2] = 0;
            m[1][3] = 0;
            m[2][0] = 0;
            m[2][1] = 0;
            m[2][2] = 0;
            m[2][3] = 0;
            m[3][0] = 0;
            m[3][1] = 0;
            m[3][2] = 0;
            m[3][3] = 0;
        }
		/**********************************************************************************************************/
        inline Matrix4(
            Real m00, Real m01, Real m02, Real m03,
            Real m10, Real m11, Real m12, Real m13,
            Real m20, Real m21, Real m22, Real m23,
            Real m30, Real m31, Real m32, Real m33 )
        {
            m[0][0] = m00;
            m[0][1] = m01;
            m[0][2] = m02;
            m[0][3] = m03;
            m[1][0] = m10;
            m[1][1] = m11;
            m[1][2] = m12;
            m[1][3] = m13;
            m[2][0] = m20;
            m[2][1] = m21;
            m[2][2] = m22;
            m[2][3] = m23;
            m[3][0] = m30;
            m[3][1] = m31;
            m[3][2] = m32;
            m[3][3] = m33;
        }
		/**********************************************************************************************************/
		inline Real* operator [] ( size_t iRow )
        {
            if( iRow >= 4 )
				throw std::invalid_argument("Matrix4 wrong index");
            return m[iRow];
        }
		/**********************************************************************************************************/
        inline const Real *const operator [] ( size_t iRow ) const
        {
            if( iRow >= 4 )
				throw std::invalid_argument("Matrix4 wrong index");
            return m[iRow];
        }
		/**********************************************************************************************************/
		inline Matrix4 Concatenate(const Matrix4 &m2) const
        {
            Matrix4 r;
            r.m[0][0] = m[0][0] * m2.m[0][0] + m[0][1] * m2.m[1][0] + m[0][2] * m2.m[2][0] + m[0][3] * m2.m[3][0];
            r.m[0][1] = m[0][0] * m2.m[0][1] + m[0][1] * m2.m[1][1] + m[0][2] * m2.m[2][1] + m[0][3] * m2.m[3][1];
            r.m[0][2] = m[0][0] * m2.m[0][2] + m[0][1] * m2.m[1][2] + m[0][2] * m2.m[2][2] + m[0][3] * m2.m[3][2];
            r.m[0][3] = m[0][0] * m2.m[0][3] + m[0][1] * m2.m[1][3] + m[0][2] * m2.m[2][3] + m[0][3] * m2.m[3][3];

            r.m[1][0] = m[1][0] * m2.m[0][0] + m[1][1] * m2.m[1][0] + m[1][2] * m2.m[2][0] + m[1][3] * m2.m[3][0];
            r.m[1][1] = m[1][0] * m2.m[0][1] + m[1][1] * m2.m[1][1] + m[1][2] * m2.m[2][1] + m[1][3] * m2.m[3][1];
            r.m[1][2] = m[1][0] * m2.m[0][2] + m[1][1] * m2.m[1][2] + m[1][2] * m2.m[2][2] + m[1][3] * m2.m[3][2];
            r.m[1][3] = m[1][0] * m2.m[0][3] + m[1][1] * m2.m[1][3] + m[1][2] * m2.m[2][3] + m[1][3] * m2.m[3][3];

            r.m[2][0] = m[2][0] * m2.m[0][0] + m[2][1] * m2.m[1][0] + m[2][2] * m2.m[2][0] + m[2][3] * m2.m[3][0];
            r.m[2][1] = m[2][0] * m2.m[0][1] + m[2][1] * m2.m[1][1] + m[2][2] * m2.m[2][1] + m[2][3] * m2.m[3][1];
            r.m[2][2] = m[2][0] * m2.m[0][2] + m[2][1] * m2.m[1][2] + m[2][2] * m2.m[2][2] + m[2][3] * m2.m[3][2];
            r.m[2][3] = m[2][0] * m2.m[0][3] + m[2][1] * m2.m[1][3] + m[2][2] * m2.m[2][3] + m[2][3] * m2.m[3][3];

            r.m[3][0] = m[3][0] * m2.m[0][0] + m[3][1] * m2.m[1][0] + m[3][2] * m2.m[2][0] + m[3][3] * m2.m[3][0];
            r.m[3][1] = m[3][0] * m2.m[0][1] + m[3][1] * m2.m[1][1] + m[3][2] * m2.m[2][1] + m[3][3] * m2.m[3][1];
            r.m[3][2] = m[3][0] * m2.m[0][2] + m[3][1] * m2.m[1][2] + m[3][2] * m2.m[2][2] + m[3][3] * m2.m[3][2];
            r.m[3][3] = m[3][0] * m2.m[0][3] + m[3][1] * m2.m[1][3] + m[3][2] * m2.m[2][3] + m[3][3] * m2.m[3][3];

            return r;
        }
		/**********************************************************************************************************/
        /**
		*   Matrix concatenation using '*'.
        */
		inline Matrix4 operator * ( const Matrix4 &m2 ) const
        {
            return Concatenate( m2 );
        }
		/**********************************************************************************************************/
        /**
		*   Vector transformation using '*'.
        */
		inline Vector3<Real> operator * ( const Vector3<Real> &v ) const
        {
            Vector3<Real> r;

            Real fInvW = 1.0f / ( m[3][0] * v.x + m[3][1] * v.y + m[3][2] * v.z + m[3][3] );

            r.x = ( m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3] ) * fInvW;
            r.y = ( m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3] ) * fInvW;
            r.z = ( m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3] ) * fInvW;

            return r;
        }
		/**********************************************************************************************************/
        /**
		*   Matrix addition.
		*/
		inline Matrix4 operator + ( const Matrix4 &m2 ) const
        {
            Matrix4 r;

            r.m[0][0] = m[0][0] + m2.m[0][0];
            r.m[0][1] = m[0][1] + m2.m[0][1];
            r.m[0][2] = m[0][2] + m2.m[0][2];
            r.m[0][3] = m[0][3] + m2.m[0][3];

            r.m[1][0] = m[1][0] + m2.m[1][0];
            r.m[1][1] = m[1][1] + m2.m[1][1];
            r.m[1][2] = m[1][2] + m2.m[1][2];
            r.m[1][3] = m[1][3] + m2.m[1][3];

            r.m[2][0] = m[2][0] + m2.m[2][0];
            r.m[2][1] = m[2][1] + m2.m[2][1];
            r.m[2][2] = m[2][2] + m2.m[2][2];
            r.m[2][3] = m[2][3] + m2.m[2][3];

            r.m[3][0] = m[3][0] + m2.m[3][0];
            r.m[3][1] = m[3][1] + m2.m[3][1];
            r.m[3][2] = m[3][2] + m2.m[3][2];
            r.m[3][3] = m[3][3] + m2.m[3][3];

            return r;
        }
		/**********************************************************************************************************/
        /**
		*   Matrix subtraction.
        */
		inline Matrix4 operator - ( const Matrix4 &m2 ) const
        {
            Matrix4 r;
            r.m[0][0] = m[0][0] - m2.m[0][0];
            r.m[0][1] = m[0][1] - m2.m[0][1];
            r.m[0][2] = m[0][2] - m2.m[0][2];
            r.m[0][3] = m[0][3] - m2.m[0][3];

            r.m[1][0] = m[1][0] - m2.m[1][0];
            r.m[1][1] = m[1][1] - m2.m[1][1];
            r.m[1][2] = m[1][2] - m2.m[1][2];
            r.m[1][3] = m[1][3] - m2.m[1][3];

            r.m[2][0] = m[2][0] - m2.m[2][0];
            r.m[2][1] = m[2][1] - m2.m[2][1];
            r.m[2][2] = m[2][2] - m2.m[2][2];
            r.m[2][3] = m[2][3] - m2.m[2][3];

            r.m[3][0] = m[3][0] - m2.m[3][0];
            r.m[3][1] = m[3][1] - m2.m[3][1];
            r.m[3][2] = m[3][2] - m2.m[3][2];
            r.m[3][3] = m[3][3] - m2.m[3][3];

            return r;
        }
		/**********************************************************************************************************/
        /**
		*   Tests 2 matrices for equality.
        */
		inline bool operator == ( const Matrix4& m2 ) const
        {
            if(
                m[0][0] != m2.m[0][0] || m[0][1] != m2.m[0][1] || m[0][2] != m2.m[0][2] || m[0][3] != m2.m[0][3] ||
                m[1][0] != m2.m[1][0] || m[1][1] != m2.m[1][1] || m[1][2] != m2.m[1][2] || m[1][3] != m2.m[1][3] ||
                m[2][0] != m2.m[2][0] || m[2][1] != m2.m[2][1] || m[2][2] != m2.m[2][2] || m[2][3] != m2.m[2][3] ||
                m[3][0] != m2.m[3][0] || m[3][1] != m2.m[3][1] || m[3][2] != m2.m[3][2] || m[3][3] != m2.m[3][3] )
                return false;
            return true;
        }
		/**********************************************************************************************************/
        /**
		*   Tests 2 matrices for inequality.
        */
		inline bool operator != ( const Matrix4& m2 ) const
        {
            if(
                m[0][0] != m2.m[0][0] || m[0][1] != m2.m[0][1] || m[0][2] != m2.m[0][2] || m[0][3] != m2.m[0][3] ||
                m[1][0] != m2.m[1][0] || m[1][1] != m2.m[1][1] || m[1][2] != m2.m[1][2] || m[1][3] != m2.m[1][3] ||
                m[2][0] != m2.m[2][0] || m[2][1] != m2.m[2][1] || m[2][2] != m2.m[2][2] || m[2][3] != m2.m[2][3] ||
                m[3][0] != m2.m[3][0] || m[3][1] != m2.m[3][1] || m[3][2] != m2.m[3][2] || m[3][3] != m2.m[3][3] )
                return true;
            return false;
        }
		/**********************************************************************************************************/
		/**
		*   Transpose matrix.
        */
		inline Matrix4 Transpose(void) const
        {
            return Matrix4(m[0][0], m[1][0], m[2][0], m[3][0],
                           m[0][1], m[1][1], m[2][1], m[3][1],
                           m[0][2], m[1][2], m[2][2], m[3][2],
                           m[0][3], m[1][3], m[2][3], m[3][3]);
        }
		/**********************************************************************************************************/
		/**
		*  Sets the translation transformation part of the matrix.
        */
		inline void SetTrans( const Vector3<Real>& v )
        {
            m[0][3] = v.x;
            m[1][3] = v.y;
            m[2][3] = v.z;
        }
		/**********************************************************************************************************/
        /**
		*   Builds a translation matrix
        */
		inline void MakeTrans( const Vector3<Real>& v )
        {
            m[0][0] = 1.0; m[0][1] = 0.0; m[0][2] = 0.0; m[0][3] = v.x;
            m[1][0] = 0.0; m[1][1] = 1.0; m[1][2] = 0.0; m[1][3] = v.y;
            m[2][0] = 0.0; m[2][1] = 0.0; m[2][2] = 1.0; m[2][3] = v.z;
            m[3][0] = 0.0; m[3][1] = 0.0; m[3][2] = 0.0; m[3][3] = 1.0;
        }
		/**********************************************************************************************************/
        inline void MakeTrans( Real tx, Real ty, Real tz )
        {
            m[0][0] = 1.0; m[0][1] = 0.0; m[0][2] = 0.0; m[0][3] = tx;
            m[1][0] = 0.0; m[1][1] = 1.0; m[1][2] = 0.0; m[1][3] = ty;
            m[2][0] = 0.0; m[2][1] = 0.0; m[2][2] = 1.0; m[2][3] = tz;
            m[3][0] = 0.0; m[3][1] = 0.0; m[3][2] = 0.0; m[3][3] = 1.0;
        }
		/**********************************************************************************************************/
        /**
		*   Builds a rotations matrix
        */
		inline void MakeRotZ( Real angle )
        {
            m[0][0] = cos(angle); m[0][1] = -sin(angle); m[0][2] = 0.0; m[0][3] = 0.0;
            m[1][0] = sin(angle); m[1][1] = cos(angle);  m[1][2] = 0.0; m[1][3] = 0.0;
            m[2][0] = 0.0;        m[2][1] = 0.0;         m[2][2] = 1.0; m[2][3] = 0.0;
            m[3][0] = 0.0;        m[3][1] = 0.0;         m[3][2] = 0.0; m[3][3] = 1.0;
        }
		/**********************************************************************************************************/
        inline void MakeRotX( Real angle )
        {
            m[0][0] = 1.0; m[0][1] = 0.0;        m[0][2] = 0.0;         m[0][3] = 0.0;
            m[1][0] = 0.0; m[1][1] = cos(angle); m[1][2] = -sin(angle); m[1][3] = 0.0;
            m[2][0] = 0.0; m[2][1] = sin(angle); m[2][2] = cos(angle);  m[2][3] = 0.0;
            m[3][0] = 0.0; m[3][1] = 0.0;        m[3][2] = 0.0;         m[3][3] = 1.0;
        }
		/**********************************************************************************************************/
        inline void MakeRotY( Real angle )
        {
			m[0][0] = cos(angle);  m[0][1] = 0.0; m[0][2] = sin(angle); m[0][3] = 0.0;
            m[1][0] = 0.0;         m[1][1] = 1.0; m[1][2] = 0.0;        m[1][3] = 0.0;
            m[2][0] = -sin(angle); m[2][1] = 0.0; m[2][2] = cos(angle); m[2][3] = 0.0;
            m[3][0] = 0.0;         m[3][1] = 0.0; m[3][2] = 0.0;        m[3][3] = 1.0;
        }
		/**********************************************************************************************************/
        inline void MakeRotFromAxis(const Vector3<Real>& vec, Real angle)
        {
			Vector3<Real> v = vec;
			v.Normalise();

			// do the trig
			Real c = cos(angle);
			Real s = sin(angle);
			Real t = 1-c;

			m[0][0] = t*v.x*v.x + c;     m[0][1] = t*v.x*v.y + s*v.z; m[0][2] = t*v.x*v.z - s*v.y; m[0][3] = 0.0;
            m[1][0] = t*v.x*v.y - s*v.z; m[1][1] = t*v.y*v.y + c;     m[1][2] = t*v.y*v.z + s*v.x; m[1][3] = 0.0;
            m[2][0] = t*v.x*v.z + s*v.y; m[2][1] = t*v.y*v.z - s*v.x; m[2][2] = t*v.z*v.z + c;     m[2][3] = 0.0;
            m[3][0] = 0.0;               m[3][1] = 0.0;               m[3][2] = 0.0;               m[3][3] = 1.0;
        }
		/**********************************************************************************************************/
        inline void MakeRotFromAxis(Real x, Real y, Real z,Real angle )
        {
			MakeRotFromAxis(Vector3<Real>(x,y,z),angle);
        }
		/**********************************************************************************************************/
		/**
		*   Builds a scale matrix
        */
		inline void MakeScale( Real sx, Real sy, Real sz )
        {
            m[0][0] = sx; m[0][1] = 0.0; m[0][2] = 0.0; m[0][3] = 0;
            m[1][0] = 0.0; m[1][1] = sy; m[1][2] = 0.0; m[1][3] = 0;
            m[2][0] = 0.0; m[2][1] = 0.0; m[2][2] = sz; m[2][3] = 0;
            m[3][0] = 0.0; m[3][1] = 0.0; m[3][2] = 0.0; m[3][3] = 1.0;
        }
		/**********************************************************************************************************/
        inline void MakeScale( const Vector3<Real>& s )
        {
            m[0][0] = s.x; m[0][1] = 0.0; m[0][2] = 0.0; m[0][3] = 0;
            m[1][0] = 0.0; m[1][1] = s.y; m[1][2] = 0.0; m[1][3] = 0;
            m[2][0] = 0.0; m[2][1] = 0.0; m[2][2] = s.z; m[2][3] = 0;
            m[3][0] = 0.0; m[3][1] = 0.0; m[3][2] = 0.0; m[3][3] = 1.0;
        }
		/**********************************************************************************************************/
        /**
		*   Gets a translation matrix.
        */
		inline static Matrix4 GetTrans( const Vector3<Real>& v )
        {
            Matrix4 r;

            r.m[0][0] = 1.0; r.m[0][1] = 0.0; r.m[0][2] = 0.0; r.m[0][3] = v.x;
            r.m[1][0] = 0.0; r.m[1][1] = 1.0; r.m[1][2] = 0.0; r.m[1][3] = v.y;
            r.m[2][0] = 0.0; r.m[2][1] = 0.0; r.m[2][2] = 1.0; r.m[2][3] = v.z;
            r.m[3][0] = 0.0; r.m[3][1] = 0.0; r.m[3][2] = 0.0; r.m[3][3] = 1.0;

            return r;
        }
		/**********************************************************************************************************/
        /**
		*   Gets a translation matrix - variation for not using a vector.
        */
		inline static Matrix4 GetTrans( Real t_x, Real t_y, Real t_z )
        {
            Matrix4 r;

            r.m[0][0] = 1.0; r.m[0][1] = 0.0; r.m[0][2] = 0.0; r.m[0][3] = t_x;
            r.m[1][0] = 0.0; r.m[1][1] = 1.0; r.m[1][2] = 0.0; r.m[1][3] = t_y;
            r.m[2][0] = 0.0; r.m[2][1] = 0.0; r.m[2][2] = 1.0; r.m[2][3] = t_z;
            r.m[3][0] = 0.0; r.m[3][1] = 0.0; r.m[3][2] = 0.0; r.m[3][3] = 1.0;

            return r;
        }
		/**********************************************************************************************************/
        /**
		*   Sets the scale part of the matrix.
        */
        inline void SetScale( const Vector3<Real>& v )
        {
            m[0][0] = v.x;
            m[1][1] = v.y;
            m[2][2] = v.z;
        }
		/**********************************************************************************************************/
        /**
		*   Gets a scale matrix.
        */
		inline static Matrix4 GetScale( const Vector3<Real>& v )
        {
            Matrix4 r;
            r.m[0][0] = v.x; r.m[0][1] = 0.0; r.m[0][2] = 0.0; r.m[0][3] = 0.0;
            r.m[1][0] = 0.0; r.m[1][1] = v.y; r.m[1][2] = 0.0; r.m[1][3] = 0.0;
            r.m[2][0] = 0.0; r.m[2][1] = 0.0; r.m[2][2] = v.z; r.m[2][3] = 0.0;
            r.m[3][0] = 0.0; r.m[3][1] = 0.0; r.m[3][2] = 0.0; r.m[3][3] = 1.0;

            return r;
        }
		/**********************************************************************************************************/
        /**
		*   Gets a scale matrix - variation for not using a vector.
        */
		inline static Matrix4 GetScale( Real s_x, Real s_y, Real s_z )
        {
            Matrix4 r;
            r.m[0][0] = s_x; r.m[0][1] = 0.0; r.m[0][2] = 0.0; r.m[0][3] = 0.0;
            r.m[1][0] = 0.0; r.m[1][1] = s_y; r.m[1][2] = 0.0; r.m[1][3] = 0.0;
            r.m[2][0] = 0.0; r.m[2][1] = 0.0; r.m[2][2] = s_z; r.m[2][3] = 0.0;
            r.m[3][0] = 0.0; r.m[3][1] = 0.0; r.m[3][2] = 0.0; r.m[3][3] = 1.0;

            return r;
        }
		/**********************************************************************************************************/
        static const Matrix4 ZERO;
		/**********************************************************************************************************/
        static const Matrix4 IDENTITY;
		/**********************************************************************************************************/
        /**
		*   Useful little matrix which takes 2D clipspace {-1, 1} to {0,1}
        *   and inverts the Y.
		*/
        static const Matrix4 CLIPSPACE2DTOIMAGESPACE;
		/**********************************************************************************************************/
        inline Matrix4 operator*(Real scalar)
        {
            return Matrix4(
                scalar*m[0][0], scalar*m[0][1], scalar*m[0][2], scalar*m[0][3],
                scalar*m[1][0], scalar*m[1][1], scalar*m[1][2], scalar*m[1][3],
                scalar*m[2][0], scalar*m[2][1], scalar*m[2][2], scalar*m[2][3],
                scalar*m[3][0], scalar*m[3][1], scalar*m[3][2], scalar*m[3][3]);
        }
		/**********************************************************************************************************/
        inline Matrix4 operator/(Real scalar)
        {
            return Matrix4(
                m[0][0]/scalar, m[0][1]/scalar, m[0][2]/scalar, m[0][3]/scalar,
                m[1][0]/scalar, m[1][1]/scalar, m[1][2]/scalar, m[1][3]/scalar,
                m[2][0]/scalar, m[2][1]/scalar, m[2][2]/scalar, m[2][3]/scalar,
                m[3][0]/scalar, m[3][1]/scalar, m[3][2]/scalar, m[3][3]/scalar);
        }
		/**********************************************************************************************************/

		Matrix4 Adjoint() const;
		Real Determinant() const;
		Matrix4 Inverse() const;

    private:
        ///	The matrix entries, indexed by [row][col].
		Real m[4][4];
		static Real mgl[16];
    };
	/*******************************************************************************/
	template<class Real>
	Real Matrix4<Real>::mgl[16];
	/*******************************************************************************/
	template<class Real>
    const Matrix4<Real> Matrix4<Real>::ZERO(
        0, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0 );
	/*******************************************************************************/
	template<class Real>
    const Matrix4<Real> Matrix4<Real>::IDENTITY(
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1 );
	/*******************************************************************************/
	template<class Real>
    const Matrix4<Real> Matrix4<Real>::CLIPSPACE2DTOIMAGESPACE(
        0.5,    0,  0, 0.5,
          0, -0.5,  0, 0.5,
          0,    0,  1,   0,
          0,    0,  0,   1);
	/*******************************************************************************/
	template<class Real>
    Real MINOR(const Matrix4<Real>& m, const size_t r0, const size_t r1, const size_t r2,
								const size_t c0, const size_t c1, const size_t c2)
    {
        return m[r0][c0] * (m[r1][c1] * m[r2][c2] - m[r2][c1] * m[r1][c2]) -
            m[r0][c1] * (m[r1][c0] * m[r2][c2] - m[r2][c0] * m[r1][c2]) +
            m[r0][c2] * (m[r1][c0] * m[r2][c1] - m[r2][c0] * m[r1][c1]);
    }
	/*******************************************************************************/
	template<class Real>
    Real Matrix4<Real>::Determinant() const
    {
        return m[0][0] * MINOR(*this, 1, 2, 3, 1, 2, 3) -
            m[0][1] * MINOR(*this, 1, 2, 3, 0, 2, 3) +
            m[0][2] * MINOR(*this, 1, 2, 3, 0, 1, 3) -
            m[0][3] * MINOR(*this, 1, 2, 3, 0, 1, 2);
    }
	/*******************************************************************************/
	template<class Real>
    Matrix4<Real>  Matrix4<Real>::Adjoint() const
    {
        return Matrix4( MINOR(*this, 1, 2, 3, 1, 2, 3),
            -MINOR(*this, 0, 2, 3, 1, 2, 3),
            MINOR(*this, 0, 1, 3, 1, 2, 3),
            -MINOR(*this, 0, 1, 2, 1, 2, 3),

            -MINOR(*this, 1, 2, 3, 0, 2, 3),
            MINOR(*this, 0, 2, 3, 0, 2, 3),
            -MINOR(*this, 0, 1, 3, 0, 2, 3),
            MINOR(*this, 0, 1, 2, 0, 2, 3),

            MINOR(*this, 1, 2, 3, 0, 1, 3),
            -MINOR(*this, 0, 2, 3, 0, 1, 3),
            MINOR(*this, 0, 1, 3, 0, 1, 3),
            -MINOR(*this, 0, 1, 2, 0, 1, 3),

            -MINOR(*this, 1, 2, 3, 0, 1, 2),
            MINOR(*this, 0, 2, 3, 0, 1, 2),
            -MINOR(*this, 0, 1, 3, 0, 1, 2),
            MINOR(*this, 0, 1, 2, 0, 1, 2));
    }
	/*******************************************************************************/
	template<class Real>
    Matrix4<Real> Matrix4<Real>::Inverse() const
    {
        return Adjoint() * (1.0f / Determinant());
    }
	/*******************************************************************************/
}

#endif
